﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Collections.ObjectModel;
using System.Data;

namespace SimpleExpression
{
    public class ConditionExpression : Expression
    {
        private LogicalOperator logicalOperator; //El logical operator que le antecede a la condicion
        private object value1;
        private ConditionOperator conditionOperator;
        private object value2;

        //XML Required
        public ConditionExpression()
        { 
        
        }

        public ConditionExpression(object value1, ConditionOperator conditionOperator, object value2)
            :this(LogicalOperator.And, value1, conditionOperator, value2)
        {

        }
        
        public ConditionExpression(LogicalOperator logicalOperator, object value1, ConditionOperator conditionOperator, object value2)
        {
            this.logicalOperator = logicalOperator;
            this.value1 = value1;
            this.conditionOperator = conditionOperator;
            this.value2 = value2;
        }

        public object Value1
        {
            get { return this.value1; }
        }

        public ConditionOperator ConditionOperator
        {
            get { return this.conditionOperator; }
        }

        public object Value2
        {
            get { return this.value2; }
        }

        public LogicalOperator LogicalOperator
        {
            get { return this.logicalOperator; }
        }

        #region Expression

        public override void Accept(ExpressionVisitor visitor)
        {
            visitor.Visit(this);
        }

        #endregion

        #region Helper Methods
        
        public string GetSQLConditionOperator()
        {
            switch (this.conditionOperator)
            {
                case ConditionOperator.Equals:
                    return "=";
                case ConditionOperator.Between:
                    return "BETWEEN";
                case ConditionOperator.GreaterThan:
                    return ">";
                case ConditionOperator.GreaterThanOrEqual:
                    return ">=";
                case ConditionOperator.LessThan:
                    return "<";
                case ConditionOperator.LessThanOrEqual:
                    return "<=";
                case ConditionOperator.In:
                    return "IN";
                case ConditionOperator.StarsWith:
                case ConditionOperator.EndsWith:
                case ConditionOperator.Contains:
                    return "LIKE";
                case ConditionOperator.Different:
                    return "<>";
                default:
                    throw new NotImplementedException();
            }
        }

        public string GetConditionField(IDbCommand command, IDatabaseProvider provider, object value)
        {
            return this.AddParameter(command, provider, value);
        }

        public string GetSQLValue(IDbCommand command, IDatabaseProvider provider, object value)
        {
            switch (this.conditionOperator)
            {
                case ConditionOperator.Equals:
                case ConditionOperator.Different:
                case ConditionOperator.GreaterThan:
                case ConditionOperator.GreaterThanOrEqual:
                case ConditionOperator.LessThan:
                case ConditionOperator.LessThanOrEqual:
                    return this.AddParameter(command, provider, value);
                case ConditionOperator.Between:
                    return string.Format("{0} AND {1}", this.AddParameter(command, provider, ((BetweenCondition)value).FirstValue), this.AddParameter(command, provider, ((BetweenCondition)value).FirstValue));
                case ConditionOperator.In:
                    ICollection<string> list = new Collection<string>();

                    if (value is IEnumerable)
                        foreach (object item in ((IEnumerable)value))
                        {
                            list.Add(this.AddParameter(command, provider, item));
                        }
                    else
                        list.Add(value.ToString());

                    return string.Format("({0})", string.Join(", ", list.ToArray()));
                case ConditionOperator.StarsWith:
                    return this.AddParameter(command, string.Format("{0}%", value));
                case ConditionOperator.EndsWith:
                    return this.AddParameter(command, string.Format("%{0}", value));
                case ConditionOperator.Contains:
                    return this.AddParameter(command, string.Format("%{0}%", value));
                default:
                    throw new NotImplementedException();
            }
        }

        public string GetValue(object value)
        {
            switch (this.conditionOperator)
            {
                case ConditionOperator.Equals:
                case ConditionOperator.Different:
                case ConditionOperator.GreaterThan:
                case ConditionOperator.GreaterThanOrEqual:
                case ConditionOperator.LessThan:
                case ConditionOperator.LessThanOrEqual:
                    return value.ToString();
                case ConditionOperator.Between:
                    return string.Format("{0} AND {1}", ((BetweenCondition)value).FirstValue, ((BetweenCondition)value).FirstValue);
                case ConditionOperator.In:
                    ICollection<object> list = new Collection<object>();
                    
                    if (value is IEnumerable)
                    {
                        foreach (object item in ((IEnumerable)value))
                        {
                            list.Add(item.ToString());
                        }
                    }
                    else
                        list.Add(value);
                   
                    return string.Format("({0})", string.Join(", ", list.ToArray()));
                case ConditionOperator.StarsWith:
                    return string.Format("{0}%", value);
                case ConditionOperator.EndsWith:
                    return string.Format("%{0}", value);
                case ConditionOperator.Contains:
                    return string.Format("%{0}%", value);
                default:
                    throw new NotImplementedException();
            }
        }

        private string AddParameter(IDbCommand command, IDatabaseProvider provider, object value)
        {
            if (value is AttributeExpression)
                return string.Format("{0}.{1}", ((AttributeExpression)value).PropertyMetadata.Alias, provider.Encode(GetPropertyName((AttributeExpression)value)));
            else
                if (value is PropertyMetadata)
                    return string.Format("{0}.{1}", ((PropertyMetadata)value).Alias, provider.Encode(((PropertyMetadata)value).PhysicalName));
                else
                    return this.AddParameter(command, value);
        }

        private string GetPropertyName(AttributeExpression attribute)
        {
            if (attribute is ViewAttributeExpression)
                return attribute.PropertyMetadata.PlatformName;
            else
                return attribute.PropertyMetadata.PhysicalName;
        }

        #endregion
    }
}
